import sys
import os
import re
from subprocess import *
from GridSearcher import GridSearcher
from GASearcher import GASearcher

class Runcase(object):
    """ this object run the train and predict procedure"""
    def __init__(self, dataDisplayer, options):
        super(Runcase, self).__init__()

        self.dataDisplayer = dataDisplayer
        self.options = options
        self.setup()

        self.kernelTypes = ['linear','polynomial','rbf','sigmoid']
        self.svmOptions = '-c {0} -g {1} -v {2} -s {3} -t {4} -d {5} -r {6} -n {7} -e {8} -p {9}'.format \
                ('cost','gamma', self.options['nfold'], self.options['svmType'], \
                self.kernelTypes.index(self.options['kernelType']), self.options['degree'],self.options['coef0'], \
                self.options['nu'],self.options['epsilon'], self.options['epsilon_SVR'])


    def setup(self):
        is_win32 = (sys.platform == 'win32')
        if not is_win32:
            self.svmscale_exe = "LibSVM/svm-scale"
            self.svmtrain_exe = "LibSVM/svm-train"
            self.svmpredict_exe = "LibSVM/svm-predict"
            self.grid_py = "LibSVM/grid.py"
            self.train_pathname = r'LibSVM/train'
            self.test_pathname = r'LibSVM/test'
        else:
            self.svmscale_exe = r"LibSVM\svm-scale.exe"
            self.svmtrain_exe = r"LibSVM\svm-train.exe"
            self.svmpredict_exe = r"LibSVM\svm-predict.exe"
            self.grid_py = r"LibSVM\grid.py"
            self.train_pathname = r'LibSVM\train'
            self.test_pathname = r'LibSVM\test'

        assert os.path.exists(self.svmscale_exe),"svm-scale executable not found"
        assert os.path.exists(self.svmtrain_exe),"svm-train executable not found"
        assert os.path.exists(self.svmpredict_exe),"svm-predict executable not found"
        assert os.path.exists(self.grid_py),"grid.py not found"

        #self.train_pathname = self.options['trainFile']
        assert os.path.exists(self.train_pathname),"training file not found"
        self.train_file_name = os.path.split(self.train_pathname)[1]
        self.path_to_data_file = os.path.split(self.train_pathname)[0]+'\\'
        self.scaled_train_file = self.train_file_name + ".scale"
        self.model_file = self.train_file_name + ".model"
        self.range_file = self.train_file_name + ".range"

#XXX
        if self.options['testFile'] == '':
            #self.test_pathname = self.options['testFile']
            self.test_pathname = r'LibSVM\test'
            self.test_file_name = os.path.split(self.test_pathname)[1]
            assert os.path.exists(self.test_pathname),"testing file not found"
            self.scaled_test_file = self.test_file_name + ".scale"
            self.predict_file = self.test_file_name + ".predict"


    def run(self):
        if self.options['kernelType'] in ('rbf', 'polynomial', 'sigmoid'):
            self.scaleTrainFile()
            self.cost, self.gamma = self.search()
            self.train()
#XXX if self.options['testFile'] == '':
            if self.options['testFile'] == '':
                self.scaleTestFile()
                self.test()
        else:
            """ linear kernel function"""
            self.scaleTrainFile()
            self.train()
            if self.options['testFile'] != '':
                self.scaleTestFile()
                self.test()


    def train(self):
        svmOptions = self.svmOptions.replace('cost', str(self.cost))
        svmOptions = svmOptions.replace('gamma', str(self.gamma))
        # since we have done cross validation, we do not need the "-v" option now
        svmOptions = re.sub("-v\s+\d\s", "", svmOptions)
        #svmOptions = re.sub("-s.*\d", "", svmOptions)
        cmdline = '%s %s "%s" "%s"' % (self.svmtrain_exe,svmOptions, self.path_to_data_file+self.scaled_train_file, \
                self.path_to_data_file+self.model_file)
        print('Training...')
        Popen(cmdline, shell = True, stdout = PIPE).communicate()
        print('Output model: %s' % self.model_file)

    def test(self):
        if self.options['searchBestFeartures']:
            pass
        else:
            """docstring for """
            cmd = '%s "%s" "%s" "%s"' % (self.svmpredict_exe, self.path_to_data_file+self.scaled_test_file, \
                    self.path_to_data_file+self.model_file, self.path_to_data_file+self.predict_file)
            print('Testing...')
            outputs = Popen(cmd, shell = True, stdout = PIPE).stdout
            print('%s' % outputs.readlines()[-1])
            print('Output prediction: %s' % self.predict_file)

    def scaleTrainFile(self):
        """docstring for scale"""
        cmd = '%s -s "%s" "%s" > "%s"' % (self.svmscale_exe, \
                self.path_to_data_file+self.range_file, self.path_to_data_file+self.train_file_name, self.path_to_data_file+self.scaled_train_file)
        print 'Scaling training data...'
        self.dataDisplayer.writeConsole('Scaling training data...')
        Popen(cmd, shell = True, stdout = PIPE).communicate()

    def scaleTestFile(self):
        """docstring for scale"""
        print 'Scaling testing data...'
        cmd = '%s -r "%s" "%s" > "%s"' % (self.svmscale_exe, self.path_to_data_file+self.range_file, \
                self.path_to_data_file+self.test_file_name, self.path_to_data_file+self.scaled_test_file)
        self.dataDisplayer.writeConsole('Scaling training data...')
        Popen(cmd, shell = True, stdout = PIPE).communicate()


    def search(self):
        # set up parameter rangeCost and rangeGamma
        # cost and gamma assigned
        cost=self.options['cost']
        gamma=self.options['gamma']
        if cost != '' and gamma != '':
            return (float(cost), float(gamma))
        # only gamma is assigned
        elif cost == '' and gamma !='':
            rangeCost = map(float, (self.options['log2cost_begin'], self.options['log2cost_end'], self.options['log2cost_step']))
            rangeGamma = map(float, (gamma, gamma, gamma))
        # only cost is assigned
        elif cost !='' and gamma == '':
            rangeCost = map(float, (cost, cost, cost))
            rangeGamma = map(float, (self.options['log2gamma_begin'], self.options['log2gamma_end'], self.options['log2gamma_step']))
        # both are not assigned
        else:
            rangeCost = map(float, (self.options['log2cost_begin'], self.options['log2cost_end'], self.options['log2cost_step']))
            rangeGamma = map(float, (self.options['log2gamma_begin'], self.options['log2gamma_end'], self.options['log2gamma_step']))

        # set up parameter cmdline
        #self.kernelTypes = ['linear','polynomial','rbf','sigmoid']
        #self.svmOptions = '-c {0} -g {1} -v {2} -s {3} -t {4} -d {5} -r {6} -n {7} -e {8} -p {9}'.format \
                #('cost','gamma', self.options['nfold'], self.options['svmType'], \
                #self.kernelTypes.index(self.options['kernelType']), self.options['degree'],self.options['coef0'], \
                #self.options['nu'],self.options['epsilon'], self.options['epsilon_SVR'])
        cmdline = '%s %s %s' % (self.svmtrain_exe,self.svmOptions, self.path_to_data_file+self.scaled_train_file)

        # search
        """ parameter search"""
        if self.options['searchMethod'] == 'Grid Search':
            searcher = GridSearcher(self.dataDisplayer, cmdline, rangeCost, rangeGamma)
        elif self.options['searchMethod'] == 'GA':
            searcher = GASearcher(self.dataDisplayer, cmdline, self.options, rangeCost, rangeGamma)
        else:
            pass

        bestCost, bestGamma = searcher.search()
        return (bestCost, bestGamma)


